#include "InputManager.h"

#include "InputListener.h"
#include "InputMessage.h"

// Singleton accessor
InputManager* InputManager::GetInstance(void)
{
	static InputManager instance;
	return &instance;
}

InputManager::InputManager(void)
{
	m_pByteKeys		= nullptr;
	m_pBytePrevKeys = nullptr;
	m_hWnd			= nullptr;

	for(unsigned int i=0; i <= 255; ++i)
		m_rgucKeyBinds[i] = 0;
}

// For classes to register as listeners
bool InputManager::Register(const InputListener* pListener)
{
	// If the parameter is null, return false
	if(!pListener) return false;

	// Check if this listener is not already in the vector
	std::vector<const InputListener*>::iterator iter;
	for(iter = m_vListeners.begin(); iter != m_vListeners.end(); ++iter)
	{
		if(pListener == *iter)
			return false;
	}

	// If it's not null and not in the vector yet, push it
	m_vListeners.push_back(pListener);
	return true;
}
bool InputManager::Unregister(const InputListener* pListener)
{
	// If the parameter is null, return false
	if(!pListener) return false;

	// Find the listener in the vector
	std::vector<const InputListener*>::iterator iter;
	for(iter = m_vListeners.begin(); iter != m_vListeners.end(); ++iter)
	{
		if(pListener == *iter)
		{
			// We found it, so let's remove it and return true
			m_vListeners.erase(iter);
			return true;
		}
	}

	// If we didn't find it return false
	return false;
}

// Initialize input devices and variables
bool InputManager::Initialize(HWND hWnd)
{
	if(!hWnd) return false;
	m_hWnd = hWnd;

	// TODO - Load any input device that the manager will need

	return true;
}

// Process an input message from WinMain's MessageProc
LRESULT CALLBACK InputManager::ProcessMessage(unsigned int uiMessage, WPARAM wParam, LPARAM lParam)
{
	// TODO - Handle all messages and send them to the listeners
	switch(uiMessage)
	{
	case WM_KEYDOWN:
	case WM_KEYUP:
		// Store what is currently on byteKeys on the previous buffer
		if(m_pBytePrevKeys)
			delete[] m_pBytePrevKeys;

		m_pBytePrevKeys = m_pByteKeys;
		m_pByteKeys = new BYTE[256];

		if (LOWORD(wParam) != WA_INACTIVE)
			GetKeyboardState(m_pByteKeys);
		else
			ZeroMemory(m_pByteKeys, 256);

		for(unsigned char ucKey = 0; ucKey <= 255; ++ucKey)
		{
			if(m_pByteKeys[ucKey] & 0x80)
			{
				if(m_pBytePrevKeys)
				{
					if(m_pBytePrevKeys[ucKey] & 0x80)
					{
						LOG(ucKey << " is down");
						// TODO: Implement a timer for the second parameter sent with the message
						SendMessage(new InputMessage(InputMessage::KEY_DOWN, ucKey, 0, m_rgucKeyBinds[ucKey]));
						break;
					}
				}

				LOG(ucKey << " was pressed");
				SendMessage(new InputMessage(InputMessage::KEY_PRESSED, ucKey, 0, m_rgucKeyBinds[ucKey]));
				break;
			}

			if(ucKey == 255) break;
		}
		break;

	case WM_MOUSEMOVE:
		SendMessage(new InputMessage(InputMessage::MOUSE_MOVE, LOWORD(lParam), HIWORD(lParam)));
		break;
	case WM_LBUTTONDOWN:
		LOG("Mouse left button is down");
		SendMessage(new InputMessage(InputMessage::MOUSE_LEFT_DOWN,
			LOWORD(lParam), HIWORD(lParam), m_rgucKeyBinds[VK_LBUTTON]));
		break;
	case WM_LBUTTONUP:
		LOG("Mouse left button is up");
		SendMessage(new InputMessage(InputMessage::MOUSE_LEFT_UP,
			LOWORD(lParam), HIWORD(lParam), m_rgucKeyBinds[VK_LBUTTON]));
		break;
	case WM_RBUTTONDOWN:
		LOG("Mouse right button is down");
		SendMessage(new InputMessage(InputMessage::MOUSE_RIGHT_DOWN,
			LOWORD(lParam), HIWORD(lParam), m_rgucKeyBinds[VK_RBUTTON]));
		break;
	case WM_RBUTTONUP:
		LOG("Mouse right button is up");
		SendMessage(new InputMessage(InputMessage::MOUSE_RIGHT_UP,
			LOWORD(lParam), HIWORD(lParam), m_rgucKeyBinds[VK_RBUTTON]));
		break;
	case WM_MBUTTONDOWN:
		LOG("Mouse middle button is down");
		SendMessage(new InputMessage(InputMessage::MOUSE_MIDDLE_DOWN,
			LOWORD(lParam), HIWORD(lParam), m_rgucKeyBinds[VK_MBUTTON]));
		break;
	case WM_MBUTTONUP:
		LOG("Mouse middle button is up");
		SendMessage(new InputMessage(InputMessage::MOUSE_MIDDLE_UP,
			LOWORD(lParam), HIWORD(lParam), m_rgucKeyBinds[VK_MBUTTON]));
		break;
	case WM_MOUSEWHEEL:
		LOG("Mouse wheel rolled vertically");
		SendMessage(new InputMessage(InputMessage::MOUSE_WHEEL_VERT, HIWORD(wParam), 0));
		break;
	case WM_MOUSEHWHEEL:
		LOG("Mouse wheel rolled horizontally");
		SendMessage(new InputMessage(InputMessage::MOUSE_WHEEL_HOR, HIWORD(wParam), 0));
		break;
	default:
		return 1;
	}

	return 0;
}

// Calls HandleInput sending the message to all objects in the vector
void InputManager::SendMessage(const InputMessage * const pInputMsg)
{
	// If the parameter is null, return
	if(!pInputMsg) return;

	// Loop through all the listeners and send the message to all of them
	std::vector<const InputListener*>::iterator iter;
	for(iter = m_vListeners.begin(); iter != m_vListeners.end(); ++iter)
	{
		((InputListener*)*iter)->HandleInput(pInputMsg);
	}
}

// Clean up memory
void InputManager::Shutdown(void)
{
	// TODO - Unregister everyone

	// TODO - Unload any input device used by the manager

	if(m_pBytePrevKeys)
	{
		delete[] m_pBytePrevKeys;
		m_pBytePrevKeys = nullptr;
	}
	if(m_pByteKeys)
	{
		delete[] m_pByteKeys;
		m_pByteKeys = nullptr;
	}
}

// Used bind keys to IDs
bool InputManager::BindKey(unsigned char ucKey, unsigned char ucID)
{
	// If the key is alrady bound to something, return false
	if(GetKeyBind(ucKey) != 0)
		return false;

	// Else, let's bind it to this ID
	m_rgucKeyBinds[ucKey] = ucID;
	return true;
}

// Used to unbind keys to IDs
bool InputManager::UnbindKey(unsigned char ucKey)
{
	// If the key already has no ID, return false
	if(GetKeyBind(ucKey) == 0)
		return false;

	// Else, let's unbind it
	m_rgucKeyBinds[ucKey] = 0;
	return true;
}